package bold.storage.component;

import bold.storage.core.DB;
import bold.storage.core.Page;
import bold.storage.core.Record;
import bold.storage.core.RecordList;
import bold.storage.splice.SqlKit;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;

public class Query {
    private String select = "*";
    private String table;
    private String tableAs;
    private HashMap<String, String> joinMap;
    private String groupBy;
    private ArrayList<Object> param = null;
    private Where where= new Where();
    private String order;
    private String limit;

    Query() {
    }

    Query(String table) {
        this.table = table;
    }

    Query(String table, String as) {
        this.table = table;
        this.tableAs = as;
    }

    public Query join(String table, String as, String on) {
        if (this.joinMap == null) {
            this.joinMap = new HashMap<>();
        }

        this.joinMap.put(table, " left join " + SqlKit.wrapCol(table) + " " + as + " on " + on);
        return this;
    }

    public Query select(String... keys) {
        String[] var2 = keys;
        int var3 = keys.length;

        for (int var4 = 0; var4 < var3; ++var4) {
            String key = var2[var4];
            this.selectSql(key);
        }

        return this;
    }

    private Query selectSql(String sql) {
        return this.selectSql(sql, null, new Object[0]);
    }

    private Query selectSql(String sql, String as, Object... values) {
        return this.selectSql(true, sql, as, values);
    }

    private Query selectWith(String sql, String as, Object... values) {
        return this.selectSql(false, sql, as, values);
    }

    private Query selectSql(boolean removeStar, String sql, String as, Object... values) {
        if (as != null) {
            sql = SqlKit.as(sql, as);
        }

        if ("*".equals(this.select) && removeStar) {
            this.select = sql;
        } else {
            this.select = SqlKit.concat(this.select, sql);
        }

        if (values != null) {
            if (this.param == null) {
                this.param = new ArrayList<>();
            }

            this.param.addAll(Arrays.asList(values));
        }

        return this;
    }

    public Query where(Where where) {
        this.where = where;
        return this;
    }

    public Query where(String key, String value) {
        this.where.eq(key, value);
        return this;
    }

    public Query pk(String pk) {
        this.where.eq(SqlKit.getPk(), pk);
        return this;
    }

    public Query limit(int begin, int size) {
        this.limit = " limit " + begin + "," + size + " ";
        return this;
    }

    public Query limit(int size) {
        this.limit = " limit " + size + " ";
        return this;
    }

    public Query orderAsc(String col) {
        return this.order(col, "asc");
    }

    public Query orderDesc(String col) {
        return this.order(col, "desc");
    }

    public Query order(String col, String sort) {
        return this.order(SqlKit.wrapCol(col) + " " + sort);
    }

    public Query orderField(String col, String... values) {
        StringBuilder sb = new StringBuilder();
        sb.append("field(").append(SqlKit.wrapCol(col)).append(",").append(SqlKit.concatString(values)).append(")");
        return this.order(sb.toString());
    }

    private Query order(String str) {
        if (this.order == null) {
            this.order = " order by ";
        } else {
            this.order = this.order + ",";
        }

        this.order = this.order + str;
        return this;
    }

    public Query groupBy(String col) {
        this.groupBy = SqlKit.wrapCol(col);
        return this;
    }

    public String sql() {
        StringBuilder sql = new StringBuilder();
        sql.append("select ").append(this.select).append(" from ")
                .append(SqlKit.wrapCol(this.table))
                .append(this.tableAs == null ? "" : " " + this.tableAs)
                .append(this.joinMap == null ? "" : this.generateJoin())
                .append(this.where.sql())
                .append(this.groupBy == null ? "" : " group by " + this.groupBy)
                .append(this.order == null ? "" : this.order)
                .append(this.limit == null ? "" : this.limit);
        return sql.toString();
    }

    private String generateJoin() {
        StringBuilder sb = new StringBuilder();
        Iterator var2 = this.joinMap.entrySet().iterator();

        while (var2.hasNext()) {
            Entry entry = (Entry) var2.next();
            sb.append(entry.getValue());
        }

        return sb.toString();
    }

    Object[] param() {
        return this.param != null ? SqlKit.join(this.param, this.where.param()) : this.where.param().toArray();
    }

    String table() {
        return this.table;
    }

    public int count() throws SQLException {
        StringBuffer countSql = new StringBuffer();
        countSql.append("select count(1) (").append(this.sql()).append(") ttt");
        return DB.queryInteger(countSql.toString(), 0, this.param());
    }

    public boolean exist() throws SQLException {
        return this.count() > 0;
    }

    public Record record() throws SQLException {
        return DB.queryOne(this.sql(), this.param());
    }

    public <T> T entity(Class<T> tClass) throws SQLException {
        return DB.query(this.sql(), tClass, this.param());
    }

    public RecordList recordList() throws SQLException {
        return DB.query(this.sql(), this.param());
    }

    public Page paginate(String pageIndex, String pageSize) throws SQLException {
        if (null == pageIndex || "".equals(pageIndex)) {
            pageIndex = "1";
        }
        if (null == pageSize || "".equals(pageSize)) {
            pageSize = "10";
        }
        return this.paginate(Integer.parseInt(pageIndex), Integer.parseInt(pageSize));
    }

    public Page paginate(int pageIndex, int pageSize) throws SQLException {
        pageIndex = pageIndex <= 0 ? 1 : pageIndex;
        pageSize = pageSize <= 0 ? 10 : pageSize;
        this.limit((pageIndex - 1) * pageSize, pageSize);
        return DB.paginate(this.sql(), pageIndex, pageSize, this.param());
    }
}
